﻿#region Copyright
// Copyright (c) 2011-2012 RaisingForce Team (Alikin Sergey)
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion

using System;
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using Aiemp.ObjectModel;
using Aiemp.Shared.Rules;
using RaisingForce.Raise.Hardware;
using RaisingForce.Raise.Presentation;
using RaisingForce.Raise.Presentation.Controls;
using RaisingForce.Raise.Presentation.Localization;
using RaisingForce.Raise.Presentation.Names;
using RaisingForce.Raise.Presentation.ViewModel;
using RaisingForce.Raise.Presentation.ViewModel.Extensions;
using RaisingForce.Raise.Presentation.ViewModel.Specialized;
using RaisingForce.RaiseHardwareTest.Hardware;
using RaisingForce.RaiseHardwareTest.Properties;
using RaisingForce.RaiseHardwareTest.ViewModel;
using RaisingForce.RaiseHardwareTest.ViewModel.Extensions;

namespace RaisingForce.RaiseHardwareTest
{
    /// <summary>
    /// Логика взаимодействия для App.xaml
    /// </summary>
    public partial class App : Application
    {
        #region Options Helpers

        private static RobotHardwareOptions tryLoadOptions()
        {
            try
            {
                return HardwareOptionsHelper.Load(
                    ApplicationHelper.HardwareConfiguration.SettingsPath);
            }
            catch (IOException)
            {
                return null;
            }
        }

        private static void trySaveOptions(
            IRobotHardwareOptions options)
        {
            Contract.Requires(options != null);
            try
            {
                HardwareOptionsHelper.Save(
                    options,
                    ApplicationHelper.HardwareConfiguration.SettingsPath);
            }
            catch (IOException)
            { }
        }

        #endregion

        #region Setting Measures Helpers

        internal static ISettingMeasures loadSettingMeasures(Settings usingSettings)
        {
            Contract.Requires(usingSettings != null);
            Contract.Ensures(Contract.Result<ISettingMeasures>() != null);
            string distanceMeasure = usingSettings.DistanceMeasureKey;
            Contract.Assume(distanceMeasure != null);
            string timeMeasure = usingSettings.TimeMeasureKey;
            Contract.Assume(timeMeasure != null);
            string angleMeasure = usingSettings.AngleMeasureKey;
            Contract.Assume(angleMeasure != null);
            return MeasureHelper.GetSettingMeasures(distanceMeasure, timeMeasure, angleMeasure);
        }

        internal static void saveSettingMeasures(
            Settings usingSettings,
            ISettingMeasures settingMeasures)
        {
            Contract.Requires(usingSettings != null);
            Contract.Requires(settingMeasures != null);
            usingSettings.DistanceMeasureKey =
                MeasureHelper.GetDistanceMeasureKey(settingMeasures.DistanceMeasure);
            usingSettings.TimeMeasureKey =
                MeasureHelper.GetTimeMeasureKey(settingMeasures.TimeMeasure);
            usingSettings.AngleMeasureKey =
                MeasureHelper.GetAngleMeasureKey(settingMeasures.AngleMeasure);
            usingSettings.Save();
        }

        #endregion

        #region Hardware Helpers

        private static RegistryKey getSelectedHardwareKey(MainViewModel vm)
        {
            Contract.Requires(vm != null);
            var selectedHardware = vm.HardwareSelector.SelectedHardware;
            return selectedHardware != null ? selectedHardware.Key : default(RegistryKey);
        }

        #endregion

        #region Localization Helpers

        private void saveLanguage(MainViewModel vm, Settings usingSettings)
        {
            Contract.Requires(vm != null);
            Contract.Requires(usingSettings != null);
            LocalizationLanguage selectedLanguage =
                vm.LanguageSelector.SelectedLanguage;
            if (selectedLanguage != null)
            {
                if (usingSettings.LanguageCultureName != selectedLanguage.CultureName)
                {
                    usingSettings.LanguageCultureName = selectedLanguage.CultureName;
                    usingSettings.Save();
                }
            }
        }

        private void updateLanguage(MainViewModel vm)
        {
            Contract.Requires(vm != null);
            LocalizationLanguage selectedLanguage =
                vm.LanguageSelector.SelectedLanguage;
            if (selectedLanguage != null)
            {
                LocalizationManager.UICulture =
                    selectedLanguage.CultureInfo;
            }
            vm.UpdateLocalization();
        }

        private void setupLanguageDependencies(MainViewModel vm, Window window)
        {
            // Update main view model DisplayName
            // vm.DisplayName must be updated before updating any command creators!
            vm.SetTitle(RaisingForce.RaiseHardwareTest.Properties.Resources.Main_Title);
            // Setup HardwareSelector
            int hardwareNameMinWidth = int.Parse(
                RaisingForce.RaiseHardwareTest.Properties.Resources.Main_HardwareNameMinWidth);
            vm.HardwareSelector.HardwareNameMinWidth = hardwareNameMinWidth;
            vm.HardwareSelector.LabelText =
                RaisingForce.RaiseHardwareTest.Properties.Resources.HardwareSelector_LabelText;
            vm.HardwareSelector.HardwareItems.UpdateLocalization();
            // Setup Commands
            int settingLabelMinWidth = int.Parse(
                RaisingForce.RaiseHardwareTest.Properties.Resources.Settings_LabelMinWidth);
            int measureSelectorsMinWidth = int.Parse(
                RaisingForce.RaiseHardwareTest.Properties.Resources.Measures_SelectorsMinWidth);
            vm.Commands = vm.CreateCommands(window,
                settings => settings.CreateInputControls(settingLabelMinWidth),
                settings => settings.CreateCommands(),
                measureSelectorsMinWidth);
            // Create LabeledCommands
            int buttonLabelMinWidth = int.Parse(
                RaisingForce.RaiseHardwareTest.Properties.Resources.Main_ButtonLabelMinWidth);
            int moveForwardMinWidth = int.Parse(
                RaisingForce.RaiseHardwareTest.Properties.Resources.Main_Move_DialogContentMinWidth);
            int rotateMinWidth = int.Parse(
                RaisingForce.RaiseHardwareTest.Properties.Resources.Main_Rotate_DialogContentMinWidth);
            vm.LabeledCommands = vm.CreateLabeledCommands(
                buttonLabelMinWidth,
                moveForwardMinWidth,
                rotateMinWidth);
            // Setup miscellaneous commands
            vm.ExportOptionsCommand = vm.CreateExportOptionsCommand(
                RaisingForce.RaiseHardwareTest.Properties.Resources.Command_Name_ExportOptions);
            vm.ImportOptionsCommand = vm.CreateImportOptionsCommand(
                RaisingForce.RaiseHardwareTest.Properties.Resources.Command_Name_ImportOptions);
            vm.AboutCommand = vm.CreateAboutCommand(
                RaisingForce.RaiseHardwareTest.Properties.Resources.Command_Name_About);
            // Setup Measures
            MeasureViewModelHelper.UpdateLocalization();
            // Setup Hardware Plugin Info
            HardwarePluginInfoCollection.Instance.UpdateLocalization();
            // Setup window Title
            // window.Title should be updated after HardwareSelector setup!
            window.Title = vm.GetHeaderString();
        }

        #endregion

        #region Window Closing Helpers

        private bool permissionToClose = false;

        private void onClosing(MainViewModel vm, Window window, CancelEventArgs e)
        {
            if (this.permissionToClose)
            {
                this.permissionToClose = false;
            }
            else if (!vm.IsDisconnected)
            {
                e.Cancel = true;
                vm.AskConfirmation(
                    RaisingForce.RaiseHardwareTest.Properties.Resources.Main_CloseConfirmation,
                    vm.DisplayName,
                    () =>
                    {
                        this.permissionToClose = true;
                        window.Close();
                    },
                    isErrorDialog: false);
            }
        }

        #endregion



        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            Release.Type = ReleaseType.Beta;
            ApplicationHelper.RegisterSupportedLanguages();

            // Select using application settings
            //  (Settings.Default are used independently in
            //   MainViewModelExtensions.ShowSettingsDialog method)
            Settings usingSettings = Settings.Default;

            // Create main window
            var settingMeasures = loadSettingMeasures(usingSettings);
            var vm = new MainViewModel(string.Empty, settingMeasures);
            vm.Options = tryLoadOptions();

            // Setup some event handlers
            vm.UsingMeasuresChanged += (s, ev) =>
                saveSettingMeasures(usingSettings, vm.UsingMeasures);
            vm.OptionsChanged += (s, ev) =>
                {
                    trySaveOptions(vm.Options);
                    RobotSavedData.Instance.ResetData();
                };

            // Add language support
            vm.LanguageSelector.AddMultipleLanguages(
                SupportedLocalizationLanguages.Instance);
            vm.LanguageSelector.SelectedLanguageChanged +=
                (s, ev) => this.saveLanguage(vm, usingSettings);
            if (!vm.LanguageSelector.TrySelectLanguage(usingSettings.LanguageCultureName))
            {   // if previous session language was not saved
                // then select language, suggested by OS
                vm.LanguageSelector.TrySelectLanguage(LocalizationManager.UICulture);
            }
            vm.LanguageSelector.SelectedLanguageChanged +=
                (s, ev) => this.updateLanguage(vm);
            this.updateLanguage(vm);

            // Add plugined hardware
            var hardwareKeys = HardwarePluginFinder.RegisterPlugins(
                ApplicationHelper.HardwareConfiguration.PluginsMask,
                pluginAction: assembly => HardwarePluginInfoCollection.Instance
                    .AddPluginInfo(assembly));
            vm.HardwareSelector.AddRegistryKeys(hardwareKeys);
            vm.HardwareSelector.SortHardwareItems();

            // ... set previous Hardware as selected
            var prevHardwareSetting = ObjectRegistryRules.KeyIsValid(usingSettings.HardwareKey) ?
                vm.HardwareSelector.HardwareItems
                .FirstOrDefault(x => x.Key == usingSettings.HardwareKey) :
                null;
            if (prevHardwareSetting != null)
            {   // previous setting was found at the list of plugins
                vm.HardwareSelector.SelectedHardware = prevHardwareSetting;
            }
            else
            {   // previous setting was not found
                RegistryKey selectedHardwareKey = getSelectedHardwareKey(vm);
                if (!selectedHardwareKey.IsEmpty())
                {   // list is not empty
                    usingSettings.HardwareKey = selectedHardwareKey.ToString();
                    usingSettings.Save();
                }
                else
                {   // list is empty, add a "Not Found" item
                    vm.SetHardwareNotFound();
                }
            }

            // ... when new Hardware is selected - save this selection
            vm.HardwareSelector.HardwareSelectionChanged +=
                (sender, eventArgs) =>
                {
                    RegistryKey selectedHardwareKey = getSelectedHardwareKey(vm);
                    if (!selectedHardwareKey.IsEmpty()
                        && selectedHardwareKey != usingSettings.HardwareKey)
                    {
                        usingSettings.HardwareKey = selectedHardwareKey.ToString();
                        usingSettings.Save();
                    }
                };
            this.DispatcherUnhandledException += (s, ev) =>
                {
                    if (!(ev.Exception is OutOfMemoryException))
                    {
                        HardwareDispatcher.ShowException(vm, ev.Exception);
                        ev.Handled = true;
                    }
                };

            // ... setup visibility options
            vm.VisibilityOptions = new MainVisibilityViewModel
            {
                CommandsAreHidden = usingSettings.HardwareCommandsAreHidden,
                LabeledCommandsAreHidden = usingSettings.ServiceCommandsAreHidden
            };
            vm.VisibilityOptions.VisibilityChanged += (s, ev) =>
                {
                    usingSettings.HardwareCommandsAreHidden = vm.VisibilityOptions.CommandsAreHidden;
                    usingSettings.ServiceCommandsAreHidden = vm.VisibilityOptions.LabeledCommandsAreHidden;
                    usingSettings.Save();
                };

            // Create size support provider for main window
            WindowSupportProvider windowSupportProvider =
                new WindowSupportProvider(
                    usingSettings,
                    "MainWindow_Height",
                    "MainWindow_Width",
                    "MainWindow_IsMaximized");

            // Show main window
            ModelWindow.Show(
                new DefaultWindowViewModel(vm),
                showMode: ModelWindowShowModeValues.Normal,
                width: Settings.Default.MainWindow_Width,
                height: Settings.Default.MainWindow_Height,
                startupLocation: WindowStartupLocation.CenterScreen,
                prologAction: window =>
                {
                    // Setup window properties
                    windowSupportProvider.AddSupportForWindow(window);
                    window.Closing += (s, ev) => this.onClosing(vm, window, ev);
                    window.Closed += (s, ev) =>
                        {
                            windowSupportProvider.SaveWindowSize();
                            windowSupportProvider.RemoveSupportForWindow();
                        };
                    window.Icon = ApplicationHelper.Icon;
                    // Setup ViewModel window-dependent properties
                    vm.LanguageUpdating += (s, ev) =>
                        this.setupLanguageDependencies(vm, window);
                    this.setupLanguageDependencies(vm, window);
                    vm.StateChanged += (s, ev) =>
                        window.Dispatcher.Invoke(new Action(() =>
                        {
                            window.Title = vm.GetHeaderString();
                            CommandManager.InvalidateRequerySuggested();
                        }));
                });
        }
    }
}
